package com.profound.notes.mvvm;

import ohos.aafwk.ability.Ability;

import java.lang.reflect.InvocationTargetException;

/**
 * @author zjwujlei
 * @date 2021/4/24
 * @email zjwujlei@sina.com
 * @description
 */
public class ViewModelProvider {

    private static final String DEFAULT_KEY =
            "com.profound.note.mvvm.ViewModelProvider.DefaultKey";

    public interface Factory{
        <T extends ViewModel> T create(Class<T> modelClass);
    }

    private final ViewModelStore mViewModelStore;
    private final Factory mFactory;

    public ViewModelProvider(ViewModelStoreOwner owner, Factory factory){
        mViewModelStore = owner.getViewModelStore();
        mFactory = factory;
    }

    public <T extends ViewModel> T get(Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

    private <T extends ViewModel> T get(String key,  Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = (mFactory).create(modelClass);
        viewModel.attachProvider(this);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

    public static class ViewModelFactory implements Factory{
        private static ViewModelFactory sInstance;


        public static ViewModelFactory getInstance() {
            if (sInstance == null) {
                sInstance = new ViewModelFactory();
            }
            return sInstance;
        }

        private Ability mAbility;

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            try {
                return modelClass.getConstructor().newInstance();
            } catch (IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

    public static class AbilityViewModelFactory implements Factory{
        private static AbilityViewModelFactory sInstance;


        public static AbilityViewModelFactory getInstance(Ability ability) {
            if (sInstance == null) {
                sInstance = new AbilityViewModelFactory(ability);
            }
            return sInstance;
        }

        private final Ability mAbility;

        public AbilityViewModelFactory(Ability ability) {
            mAbility = ability;
        }
        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            try {
                return modelClass.getConstructor(Ability.class).newInstance(mAbility);
            } catch ( NoSuchMethodException e) {
                try {
                    return modelClass.getConstructor().newInstance();
                } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException illegalAccessException) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }   catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

}
